﻿using P2PChatApp.Utils;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Timers;


namespace P2PChatApp
{
    public partial class MainForm : Form
    {
        // Client: false, Server: true. default client
        private bool ClientOrServer = false;

        // -------------服务端----------------
        // 监听客户端的连接请求
        private TcpListener tcpListener;
        // 负责监听的线程
        private Thread listenThread;
        // 客户端套接字集合
        private Dictionary<string, Socket> dicClientSockets = new Dictionary<string, Socket>();
        // 接收消息的线程集合
        private Dictionary<string, Thread> dicReceiveMsgThreads = new Dictionary<string, Thread>();
        private List<String> clientAddress = new List<String>();

        private static string OFFLINE_FLAG = ":OFFLINE.FLAG:";

        // -------------客户端----------------
        private TcpClient tcpClient;
        private Socket clientSocket;


        public MainForm()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            List<string> IPv4List = NICUtil.GetAllIPv4Address();
            lbMessageContainer.Items.Clear();
            cbLocalIP.Items.Clear();
            cbLocalIP.Items.AddRange(IPv4List.ToArray());
            cbLocalIP.SelectedIndex = IPv4List.Count - 1;
        }

        private void btnWaitConnection_ClickAsync(object sender, EventArgs e)
        {
            // 修改程序角色为 服务端
            ClientOrServer = true;

            String stopConnection = "断开连接";
            String waitConnection = "等待连接";

            if (waitConnection.Equals(btnWaitConnection.Text))// 等待连接
            {
                if (String.IsNullOrEmpty(txtLocalPort.Text.ToString())
                || String.IsNullOrEmpty(cbLocalIP.Text.ToString()))
                {
                    ShowPromtInfo("本地端口或IP不能为空！");
                    return;
                }
                btnWaitConnection.Text = stopConnection;
                clientAddress.Clear();

                String localIp = cbLocalIP.Text;
                int localPort = int.Parse(txtLocalPort.Text.ToString());

                tcpListener = new TcpListener(IPAddress.Parse(localIp), localPort);
                // 开启连接监听
                tcpListener.Start();

                // 创建监听线程的委托：delegate
                ThreadStart threadStart = new ThreadStart(ListenConnectionRequest);
                // 创建一个监听线程
                listenThread = new Thread(threadStart);
                // 守护线程
                listenThread.IsBackground = true;
                listenThread.Start();
                ShowPromtInfo("服务端开启，正在监听:[" + localPort + "]端口！");
            }
            else // 断开连接
            {
                btnWaitConnection.Text = waitConnection;

                tcpListener.Stop();

                // 断开所有客户端的连接
                byte[] arrMsg = Encoding.UTF8.GetBytes(OFFLINE_FLAG);
                for (int i = 0; i < clientAddress.Count; i++)
                {
                    // 给每个在线的客户端发送下线标记，并移除容器中的Sokcet实例
                    dicClientSockets[clientAddress[i]].Send(arrMsg);
                    dicClientSockets[clientAddress[i]].Close();
                }
                ShowPromtInfo("服务端已经关闭！");
            }
        }

        private void ListenConnectionRequest()
        {
            while (true)
            {
                try
                {
                    Socket clientSocket = tcpListener.AcceptSocket();
                    // 创建一个线程委托
                    ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ServerReceiveMsg);
                    Thread receiveMsgThread = new Thread(threadStart);
                    // 设置为守护线程
                    receiveMsgThread.IsBackground = true;
                    // 创建线程并将clientSocket套接字传入
                    receiveMsgThread.Start(clientSocket);

                    string key = clientSocket.RemoteEndPoint.ToString();
                    // 将套接字与线程存入容器
                    dicClientSockets.Add(key, clientSocket);
                    dicReceiveMsgThreads.Add(key, receiveMsgThread);
                    // 将连接上的客户端列表显示到在线列表
                    clientAddress.Add(key);
                }
                catch
                {
                }
            }
        }

        private void ServerReceiveMsg(object obj)
        {
            Socket clientSocket = (Socket)obj;
            string strRemoteEndPoint = clientSocket.RemoteEndPoint.ToString();

            while (true)
            {
                try
                {
                    byte[] bufferMsg = new byte[1024 * 1024];
                    // 实际接收到的数据长度
                    int msgLength = clientSocket.Receive(bufferMsg);
                    // 转换为UTF-8格式
                    string strMsg = Encoding.UTF8.GetString(bufferMsg, 0, msgLength);

                    if (msgLength != 0 && strMsg == OFFLINE_FLAG)
                    {
                        clientAddress.Remove(strRemoteEndPoint);
                        dicClientSockets.Remove(strRemoteEndPoint);
                        dicReceiveMsgThreads.Remove(strRemoteEndPoint);
                    }
                    else if (msgLength != 0)
                    {
                        AddMessageToListBox(false, strMsg);
                    }

                }
                catch
                {
                    // 若出错，则清空集合中对应的套接字
                    clientAddress.Remove(strRemoteEndPoint);
                    dicClientSockets.Remove(strRemoteEndPoint);
                    dicReceiveMsgThreads.Remove(strRemoteEndPoint);
                    break;
                }
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(txtMessageBox.Text))
            {
                // 原始消息
                String originMessage = txtMessageBox.Text;
                // 处理后消息
                var postProcessingMessage = Encoding.UTF8.GetBytes(originMessage);

                if (ClientOrServer)// Server Send Message
                {
                    for (int i = 0; i < clientAddress.Count; i++)
                    {
                        // 通过dicClientSockets容器，获取容器中每一个在线的客户端
                        // 遍历出来之后一一发送
                        dicClientSockets[clientAddress[i]].Send(postProcessingMessage);
                    }

                }
                else // Client Send Message
                {
                    clientSocket.Send(postProcessingMessage);
                }
                
                AddMessageToListBox(true, originMessage);
                txtMessageBox.Clear();
                txtMessageBox.Focus();
            }
            else
            {
                ShowPromtInfo("未连接或消息不能为空！");
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            // 修改程序角色为 客户端
            ClientOrServer = false;

            String connection = "连接";
            String close = "断开";

            if (connection.Equals(btnConnect.Text))
            {
                if (String.IsNullOrEmpty(txtRemoteIP.Text.ToString())
                || String.IsNullOrEmpty(txtRemotePort.Text.ToString()))
                {
                    ShowPromtInfo("目标端口或IP不能为空！");
                    return;
                }
                btnConnect.Text = close;

                txtRemoteIP.Enabled = false;
                txtRemotePort.Enabled = false;
                
                String remoteIP = txtRemoteIP.Text.Trim();
                int remotePort = int.Parse(txtRemotePort.Text.Trim());


                tcpClient = new TcpClient(remoteIP, remotePort);
                clientSocket = tcpClient.Client;

                // 创建一个线程委托
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ClientReceiveMsg);
                Thread receiveMsgThread = new Thread(threadStart);
                // 设置为守护线程
                receiveMsgThread.IsBackground = true;
                clientSocket.ReceiveTimeout = 1000;
                clientSocket.SendTimeout = 1000;
                // 创建线程并将clientSocket套接字传入
                receiveMsgThread.Start(clientSocket);

                byte[] arrClientSendMsg = Encoding.UTF8.GetBytes("Client连接成功！");
                clientSocket.Send(arrClientSendMsg);

                ShowPromtInfo("服务器连接成功！");

            }
            else
            {
                btnConnect.Text = connection;

                txtRemoteIP.Enabled = true;
                txtRemotePort.Enabled = true;

                // 这里使用简单的办法实现客户端的下线：定义一个不容易重复的下线标志，发送给服务器
                byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(OFFLINE_FLAG);
                clientSocket.Send(arrClientSendMsg);
                ShowPromtInfo("断开连接！");
                if (tcpClient != null)
                {
                    tcpClient.Close();
                }
            }
        }

        private void ClientReceiveMsg(object? obj)
        {
            Socket serverSocket = (Socket)obj;
            string strRemoteEndPoint = serverSocket.RemoteEndPoint.ToString();
            while (true)
            {
                try
                {
                    byte[] bufferMsg = new byte[1024 * 1024];
                    int msgLength = serverSocket.Receive(bufferMsg);
                    string strMsg = Encoding.UTF8.GetString(bufferMsg, 0, msgLength);

                    if (msgLength != 0 && strMsg == OFFLINE_FLAG)
                    {
                        btnConnect.Text = "连接";
                        ShowPromtInfo("服务器已经下线，断开连接！");
                        serverSocket.Close();
                    }
                    else if (msgLength != 0)
                    {
                        AddMessageToListBox(false, strMsg);
                    }
                }
                catch (Exception ex)
                {

                }

            }
        }


        // 右下角提示停息
        private void ShowPromtInfo(String message)
        {
            promptInfo.Text = message;
            System.Timers.Timer timer = new System.Timers.Timer(3 * 1000);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerStop);
            timer.AutoReset = false;
            timer.Enabled = true;

        }
        private void TimerStop(object? sender, ElapsedEventArgs e)
        {
            promptInfo.Text = "prompt Infomation";
        }

        
        // 添加消息到ListBox中
        private void AddMessageToListBox(bool isSelf, string message)
        {
            BeginInvoke(() => {
                lbMessageContainer.Items.Add(formatMessage(isSelf, message));
            });
        }

        // 格式化消息
        private String formatMessage(bool isSelf, String message)
        {
            // (myself)[19:39:06]: test test
            // (remote)[19:39:06]: test test
            if (isSelf)
            {
                message = "(myself)[" + getCurrentTime() + "]: " + message;
            }
            else
            {
                message = "(remote)[" + getCurrentTime() + "]: " + message;
            }
            return message;
        }
        // 获取当前事件
        private String getCurrentTime()
        {
            return DateTime.Now.ToString("HH:mm:ss");
        }


    }
}